﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Ordinaire.Security.Data;

namespace Ordinaire.Security
{
    /// <summary>
    /// Manages storage of role membership information for an ASP.NET application in a SQL Server database.
    /// </summary>
    public class RoleManager
    {
        #region Variables

        /// <summary>
        /// Instance of ClickPOS.Security.RoleDataManager.
        /// </summary>
        private RoleDataManager dataManager;

        #endregion

        #region Constructor

        /// <summary>
        /// Creates an instance of RoleManager class.
        /// </summary>
        public RoleManager()
        {
            dataManager = new RoleDataManager();
        }

        #endregion

        #region Public methods

        /// <summary>
        /// Adds the specified user names to the specified roles.
        /// </summary>
        /// <param name="usernames">a string array of user names to be added to the specified roles</param>
        /// <param name="roleNames">a string array of role names to add the specified user names to</param>
        public void AddUsersToRoles(string[] usernames, string[] roleNames)
        {
            usernames.ThrowIfContainsNullOrEmpty<ArgumentException>(String.Format(ExceptionMessage.ContainsNullOrEmpty, "usernames"));
            roleNames.ThrowIfContainsNullOrEmpty<ArgumentException>(String.Format(ExceptionMessage.ContainsNullOrEmpty, "roleNames"));

            // Validates if specified usernames already assigned with any of the specified roles.
            // If true, then throw an exception.
            foreach (string username in usernames)
            {
                string[] assignedRoles = GetRolesForUser(username);
                foreach (string roleName in roleNames)
                {
                    if (assignedRoles.ToList().Contains(roleName))
                    {
                        throw ExceptionFactory.Create<ArgumentException>(
                            ExceptionMessage.AlreadyExists,
                            String.Format("Assignment of both username '{0}' and role name '{1}'", username, roleName)
                        );
                    }
                }
            }

            dataManager.InsertUserRoles(usernames, roleNames);
        }

        /// <summary>
        /// Adds a new role to the data source.
        /// </summary>
        /// <param name="roleName">the name of the role to create</param>
        public void CreateRole(string roleName)
        {
            roleName.ThrowIfNullOrEmpty<ArgumentException>(String.Format(ExceptionMessage.IsNullOrEmpty, "roleName"));

            bool exists = dataManager.Exists(roleName);
            if (exists)
            {
                throw ExceptionFactory.Create<ArgumentException>(ExceptionMessage.AlreadyExists, String.Format("Role name '{0}'", roleName));
            }

            Roles data = dataManager.Insert(roleName);
            if (data.Id == 0)
            {
                throw ExceptionFactory.Create<OException>(ExceptionMessage.Failure, "Creating new role");
            }
        }

        /// <summary>
        /// Gets a list of all roles.
        /// </summary>
        /// <returns>a string array containing the names of all the roles stored in the data source</returns>
        public string[] GetAllRoles()
        {
            var extractNames = from data in dataManager.GetAll()
                               select data.Name;
            return extractNames.ToArray();
        }

        /// <summary>
        /// Gets a list of the roles that a specified user is in.
        /// </summary>
        /// <param name="username">the user to return a list of roles for</param>
        /// <returns>a string array containing the name of all the roles for the specified user</returns>
        public string[] GetRolesForUser(string username)
        {
            username.ThrowIfNullOrEmpty<ArgumentException>(String.Format(ExceptionMessage.IsNullOrEmpty, "username"));

            List<Roles> data = dataManager.GetAllForUser(username);
            var extractNames = from record in data
                               select record.Name;
            return extractNames.ToArray();
        }

        /// <summary>
        /// Gets a value indicating whether the specified role name already exists in the role data source.
        /// </summary>
        /// <param name="roleName">the name of the role to search for in the data source</param>
        /// <returns>true if the role name already exists in the data source; otherwise, false</returns>
        public bool RoleExists(string roleName)
        {
            return dataManager.Exists(roleName);
        }

        #endregion
    }
}
